package com.luisliuyi.demo.optimize.bitmap03;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.Scroller;

import androidx.annotation.Nullable;

import java.io.IOException;
import java.io.InputStream;

public class LYBigView extends View implements View.OnTouchListener, GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener {

    //图片缩放因子
    private float mScale;

    //需要显示的区域
    private Rect mRect;

    //区域解码器
    private BitmapRegionDecoder mDecode;

    private BitmapFactory.Options mOptions;

    //图片的宽度
    private int mImageWidth;
    //图片的高度
    private int mImageHeight;
    //控件的宽度
    private int mViewWidth;
    //控件的高度
    private int mViewHeight;

    private GestureDetector mGestureDetector;
    //缩放功能
    ScaleGestureDetector mScaleGestureDetector;

    private float originalScale;
    //需要展示的图片，是被复用的
    private Bitmap mBitmap;

    //滑动帮助类
    private Scroller mScroller;

    private Matrix matrix = new Matrix();

    public LYBigView(Context context) {
        super(context);
    }

    public LYBigView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        mRect = new Rect();
        mOptions = new BitmapFactory.Options();
        mGestureDetector = new GestureDetector(context,this);
        mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGesture());
        mScroller = new Scroller(context);
        setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent motionEvent) {
        mGestureDetector.onTouchEvent(motionEvent);
        mScaleGestureDetector.onTouchEvent(motionEvent);
        return true;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        mRect.offset((int)distanceX,(int)distanceY);
        if (mRect.bottom > mImageHeight) {
            mRect.bottom = mImageHeight;
            mRect.top = mImageHeight-(int)(mViewHeight/mScale);
        }
        if(mRect.top < 0){
            mRect.top = 0;
            mRect.bottom = (int)(mViewHeight/mScale);
        }

        if(mRect.left < 0) {
            mRect.left = 0;
            mRect.right = (int)(mViewWidth/mScale);
        }
        if(mRect.right > mImageWidth) {
            mRect.right = mImageWidth;
            mRect.left = mImageWidth-(int)(mViewWidth/mScale);
        }
        invalidate();
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        mScroller.fling(mRect.left,mRect.top,(int)velocityX,-(int)velocityY,0,
                mImageWidth-(int)(mViewWidth/mScale),0,mImageHeight-(int)(mViewHeight/mScale));
        return false;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if(mScroller.isFinished()){
            return;
        }
        if (mScroller.computeScrollOffset()) {
            mRect.top = mScroller.getCurrY();
            mRect.bottom = mRect.top+(int)(mViewHeight/mScale);
            invalidate();
        }
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        return false;
    }

    // 处理缩放的回调事件
    class ScaleGesture extends ScaleGestureDetector.SimpleOnScaleGestureListener{
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scale = mScale;
            scale +=  detector.getScaleFactor()-1;
            if(scale <= originalScale){
                scale = originalScale;
            }else if(scale > originalScale*2){
                scale = originalScale*2;
            }
            mRect.right = mRect.left + (int)(mViewWidth/scale);
            mRect.bottom = mRect.top+(int)(mViewHeight/scale);
            mScale = scale;
            invalidate();
            return super.onScale(detector);
        }
    }

    public void setImage(InputStream is) {
        mOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(is, null, mOptions);
        mImageWidth = mOptions.outWidth;
        mImageHeight = mOptions.outHeight;
        //开启复用内存
        mOptions.inMutable = true;
        mOptions.inPreferredConfig = Bitmap.Config.RGB_565;
        //第二次配置才会加载像素
        mOptions.inJustDecodeBounds = false;
        try {
            mDecode = BitmapRegionDecoder.newInstance(is, false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //刷新
        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mViewWidth = getMeasuredWidth();
        mViewHeight = getMeasuredHeight();
        mRect.left = 0;
        mRect.top = 0;
        mRect.right = Math.min(mImageWidth, mViewWidth);
        mRect.bottom = Math.min(mImageHeight,mViewHeight);
        // 再定义一个缩放因子
        originalScale = mViewWidth/(float)mImageWidth;
        mScale = originalScale;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (null == mDecode) {
            return;
        }
        mOptions.inBitmap = mBitmap;
        matrix.setScale(mScale, mScale);
        mBitmap = mDecode.decodeRegion(mRect, mOptions);
        canvas.drawBitmap(mBitmap, matrix, null);
    }
}
